AWS Transfer Family for FTPを利用し、AWS PrivateLink 経由でファイル転送してみた
はじめに
AWS Transfer Family for FTPをを利用し、AWS PrivateLink経由でファイル転送する構成を構築したので、手順をまとめました。
Transfer Family for FTPは、VPC内部からしか利用できません。
そのため、Transfer Family for FTPとは別のVPC上にFTP接続するEC2クライアントが存在する場合、VPC同士をVPCピアリングなどで接続するとファイル転送できます。VPCのCIDRが重複している場合は接続できないですが、AWS PrivateLinkを利用することで重複したVPCからでもファイル転送ができます。
Transfer Familyの概要は、下記をご参考ください。
AWS PrivateLinkの概要は、下記をご参考ください。
構成
構成図は下記の通りです。
構成の補足です。詳細は、構築手順で説明します
- 2つのVPCは、同一アカウント(別アカウントでも可能)
- サブネット上に作成されるリソースはすべて同じ(ap-northeast-1a)
- Transfer Family for FTPの仕様
- 認証方法は、カスタムIDプロバイダー(AWS Lambda)
- ファイル転送先はS3バケット
- S3バケットやサブネットなどは作成済み
構築の流れ
構築の流れは、下記の通りです
- IAMロール作成
- Lambda作成
- セキュリティグループ作成
- Transfer Family作成
- Lambdaのアクセス権限設定
- NLBとターゲットグループ作成
- Transfer FamilyのPassiveIpを変更
- エンドポイントサービス作成
- VPCエンドポイント作成
- エンドポイント接続承諾
- EC2からFTP接続
IAMロール作成
Transfer Family経由でS3バケットを利用するためのIAMロールを作成します。
AmazonS3FullAccess
ポリシーをアタッチします。
名前はTransfer-Family-Role
としました。
Lambda作成
Transfer Familyのユーザー管理は、今回カスタムIDプロバイダー を利用するため、認証用のLambdaを作成します。
本来であれば、SecretManagerを利用する方がよいですが、今回は検証のため、Lambdaのコード内にユーザー情報を入れます。
Pythonコードは下記の通りです。最小コードにしています。あくまでもサンプルコードとご認識ください。IAMロールのARNは、各々変えます。
def lambda_handler(event, context): if event["username"] == "class" and event["password"] == "method": return { 'Role': 'arn:aws:iam::アカウントID:role/Transfer-Family-Role' } return {}
処理としては、ユーザー情報を判定し、Transfer Familyにロール情報を渡しています。
後ほど、Transfer FamilyがLambdaを呼ぶための権限をLambdaに設定します。Transfer FamilyのARNが必要なため、Transfer Family作成後に行います。
セキュリティグループ作成
下記リソース用のセキュリティグループをそれぞれ作成します。インバウンドルールは3つとも同じです。
- Transfer Family VPCエンドポイント
- NLB
- PrivateLinkの利用側のVPCエンドポイント
FTP通信のため、ポート21(コントロールチャネル)とポート範囲8192〜8200(データチャネル)が利用できるように、インバウンドルールを下記の通り設定します。
Transfer Family作成
Transfer Family サーバーを作成します。
プロトコルはFTPを選択します。
IDプロバイダーは、カスタムIDプロバイダーを選択します。
先程作成したLambdaを選択します。
エンドポイントタイプは、VPCの内部を選択します。2AZのサブネットを指定していますが、1つでも構いません。先程作成したセキュリティグループを指定します。
ドメインは、S3を選択します。
そのほかはデフォルト設定で作成します。
作成後、Transfer FamilyのVPCエンドポイントのプライベートIPは、10.0.3.225
と10.0.4.82
であることが確認できました。
NLBの作成時に改めて解説しますが、NLBのターゲットグループは、Transfer FamilyのVPCエンドポイントを1AZしか指定できないため、今回は10.0.3.225
を指定します。
ここまでの構築リソースは下記の通りです。
Lambdaのアクセス権限設定
Transfer FamilyがLambdaを呼ぶための権限をLambdaに設定します。
Lambdaのリソースベースポリシーステートメント
からアクセス権限を追加をクリックします。
下記の通りに入力します
- ステートメントID(一意であればよい)
transferfamily-GetUserConfigLambdaPermission
- プリンシパル
transfer.amazonaws.com
- ソースのARN(作成したTransfer FamilyのARN)
arn:aws:transfer:ap-northeast-1:アカウントID:server/Transfer FamilyのサーバーID
- アクション
lambda:InvokeFunction
NLBとターゲットグループ作成
FTP通信を行うので、ターゲットグループには、ポート21、8192~8200を指定し、ターゲットIPは10.0.3.225
にします。
ターゲットグループは、1つずつポートを指定するため、合計10つのターゲットグループを作成する必要があります。
ターゲットIPは1つしか指定できないため、Transfer Family VPCエンドポイントの1AZしか指定できません。
AWS CloudShellから10個のターゲットグループをCLIを実行し作成します。
// 以下2つは個々に設定を変えて下さい。 $ TARGET_IP="10.0.3.225" $ VPC_ID="vpc-xxxx" $ for port in 21 8192 8193 8194 8195 8196 8197 8198 8199 8200 do # ターゲットグループを作成 TARGET_GROUP_ARN=$(aws elbv2 create-target-group \ --name tf-ftp-$port \ --protocol TCP \ --port $port \ --health-check-protocol TCP \ --target-type ip \ --vpc-id $VPC_ID \ --query "TargetGroups[0].TargetGroupArn" \ --output text) # ターゲットグループにIPアドレスを登録 aws elbv2 register-targets \ --target-group-arn $TARGET_GROUP_ARN \ --targets Id=$TARGET_IP,Port=$port done
ターゲットグループが10個作成できました。
続いて、内部用のNLBを作成し、リスナー10個追加します。先程作成したターゲットグループに転送するようにします。
1サブネット上でNLBを作成します。
// 以下3つは個々に設定を変えて下さい。 $ NLB_NAME="transfer-family-nlb" $ SUBNET_ID="subnet-xxxxx" $ NLB_SG_ID="sg-xxxxx" $ NLB_ARN=$(aws elbv2 create-load-balancer \ --name $NLB_NAME \ --type network \ --scheme internal \ --subnets $SUBNET_ID \ --security-groups $NLB_SG_ID \ --query 'LoadBalancers[0].LoadBalancerArn' \ --output text) $ echo "NLB ARN: $NLB_ARN" NLB ARN: arn:aws:elasticloadbalancing:ap-northeast-1:xxx:loadbalancer/net/my-nlb/xxxxx $ for port in 21 8192 8193 8194 8195 8196 8197 8198 8199 8200 do # ターゲットグループのARNを取得 TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups \ --names tf-ftp-$port \ --query "TargetGroups[0].TargetGroupArn" \ --output text) # リスナーをNLBに追加 aws elbv2 create-listener \ --load-balancer-arn $NLB_ARN \ --protocol TCP \ --port $port \ --default-actions Type=forward,TargetGroupArn=$TARGET_GROUP_ARN done
NLBが作成できました。
各ターゲットグループのターゲットIPは、Transfer Family VPCエンドポイントのプライベートIP(10.0.3.225
)が指定されていることも確認できます。
ここまでの構築リソースは下記の通りです。
Transfer FamilyのPassiveIpを変更
変更が必要かどうかの判断は下記の記事を参考下さい。
NLB経由でFTP接続するためには、Transfer FamilyのPassiveIpは、NLBのプライベートIPを指定するケースがあります。AWSドキュメントでは、一部のFTPクライアントでPassiveIpを0.0.0.0
と指定も可能と記載がありますが、今回はNLBのプライベートIPを指定します。
まず、NLBのプライベートIPをマネジメントコンソールのネットワークインターフェイスから確認します。
10.0.3.119
であることがわかりました。
Transfer Familyの[追加設定]から、PassiveIpを10.0.3.119
に変更します。
設定変更後は、再起動する必要があります。停止した後、開始することで再起動したことになります。
PassiveIp 値を変更した場合、変更を反映するには、転送ファミリーサーバーを停止してから再起動する必要があります。 引用
エンドポイントサービス作成
サービス提供側(NLBが存在するVPC側)でエンドポイントサービスを作成します。
作成したNLBを指定します。ほかはデフォルトのままです。
作成後、サービス名を控えておきます。
もし、サービス利用者側(クライアント側)が別のアカウントの場合、プリンシパルを許可します。
今回プリンシパルは、アカウント内のすべてのプリンシパルを設定しました。VPCエンドポイントサービスの利用をIAMユーザーやIAMロールに制限することも可能です。
- AWS アカウント (およびアカウント内のすべてのプリンシパル)
arn:aws:iam::aws-account-id:root
- 特定の IAM ユーザー
arn:aws:iam::aws-account-id:user/user-name
- 特定の IAM ロール
arn:aws:iam::aws-account-id:role/role-name
VPCエンドポイント作成
サービス利用者側(クライアント側)でVPCエンドポイントを作成します。
先程のエンドポイントサービスのサービス名を入力し、[サービスの検証]をします。
作成したセキュリティグループを指定します。
作成後、VPCエンドポイントのDNS名を控えておきます。
エンドポイント接続承諾
サービス提供側のエンドポイントサービスで、エンドポイントの承諾をします。
これでPrivateLinkの設定は完了です。
ここまでの構築リソースは下記の通りです。
EC2からFTP接続
サービス提供側で、EC2(Amazon Linux 2)を起動します。
EC2でlftpパッケージをインストールし、アップロード用のtest.txt
ファイルを作成しておきます。
$ sudo yum install -y lftp ~中略~ Installed: lftp-4.4.8-12.amzn2.1.x86_6 Complete! $ echo "test" > test.txt
VPCエンドポイントのDNS名を指定してFTP接続を実行し、Lambdaに設定しているユーザー名とパスワードを入力しログインします。
$ lftp vpce-0f95aea68fb6d5ba8-hpr7z8e8.vpce-svc-04ec99342c0977f6c.ap-northeast-1.vpce.amazonaws.com // login ユーザー名 lftp vpce-0f95aea68fb6d5ba8-hpr7z8e8.vpce-svc-04ec99342c0977f6c.ap-northeast-1.vpce.amazonaws.com:~> login class // パスワード Password:
Transfer Familyでは、PASV レスポンスの際、Transfer FamilyのVPC エンドポイントのプライベートIPアドレスとポート番号をクライアントに通知します。 クライアントがこの IP アドレスに対してデータ転送用コネクションの確立を試みるため、NLB 経由でのデータ転送用コネクションが確立できません。
クライアントが拡張パッシブモード(EPSV)を利用できる場合、レスポンスがポート番号のみとなり、上記のプライベート IP アドレスが返却され利用する問題が発生しないため、NLB 経由での接続が可能となります。
現状 lftpコマンドは、拡張パッシブモードをサポートしていますが、ftp コマンドではサポートされていません。
パッシブモードの応答については下記の記事が分かりやすいです。
// 拡張パッシブモード有効化 set ftp:prefer-epsv true lftp class@vpce-0f95aea68fb6d5ba8-hpr7z8e8.vpce-svc-04ec99342c0977f6c.ap-northeast-1.vpce.amazonaws.com:~> set ftp:prefer-epsv true
デフォルトで拡張パッシブモード有効化されていましたので、上記のコマンド実行は不要でした。
それではファイルをアップロードしてみます。
lftp class@vpce-0f95aea68fb6d5ba8-hpr7z8e8.vpce-svc-04ec99342c0977f6c.ap-northeast-1.vpce.amazonaws.com:/> cd s3バケット名 lftp class@vpce-0f95aea68fb6d5ba8-hpr7z8e8.vpce-svc-04ec99342c0977f6c.ap-northeast-1.vpce.amazonaws.com:/s3バケット名> put test.txt 9 bytes transferred lftp class@vpce-0f95aea68fb6d5ba8-hpr7z8e8.vpce-svc-04ec99342c0977f6c.ap-northeast-1.vpce.amazonaws.com:/s3バケット名> ls -rwxr--r-- 1 - - 0 Sep 7 11:57 test.txt
アップロードできることが確認できました。
最後に
AWS Transfer Family for FTPをAWS PrivateLink経由でFTP接続する構成を構築手順をご紹介しました。
今回は、単一AZでの構成のため、マルチAZに比べ、可用性は低い点に注意です。
また、Transfer Familyで利用するプロトコルは、FTPではなくSFTPでのファイル転送がよいです。どうしてもFTPを利用しなければならないときに、参考になれば幸いです。